package com.gcloud.controller.network.provider.impl;

import com.gcloud.common.util.StringUtils;
import com.gcloud.controller.ResourceStates;
import com.gcloud.controller.network.dao.NetworkDao;
import com.gcloud.controller.network.dao.RouterDao;
import com.gcloud.controller.network.dao.SubnetDao;
import com.gcloud.controller.network.entity.Network;
import com.gcloud.controller.network.entity.Port;
import com.gcloud.controller.network.entity.Router;
import com.gcloud.controller.network.entity.Subnet;
import com.gcloud.controller.network.enums.NetworkType;
import com.gcloud.controller.network.model.CreateVSwitchParams;
import com.gcloud.controller.network.model.VSwitchProxyData;
import com.gcloud.controller.network.provider.ISubnetProvider;
import com.gcloud.controller.network.service.INetworkService;
import com.gcloud.controller.network.service.IPortService;
import com.gcloud.controller.network.service.IRouterService;
import com.gcloud.controller.network.service.ISubnetService;
import com.gcloud.controller.network.service.IVpcService;
import com.gcloud.controller.provider.NeutronProviderProxy;
import com.gcloud.core.exception.GCloudException;
import com.gcloud.core.simpleflow.Flow;
import com.gcloud.core.simpleflow.FlowDoneHandler;
import com.gcloud.core.simpleflow.NoRollbackFlow;
import com.gcloud.core.simpleflow.SimpleFlowChain;
import com.gcloud.core.util.BeanUtil;
import com.gcloud.header.api.model.CurrentUser;
import com.gcloud.header.enums.ProviderType;
import com.gcloud.header.enums.ResourceType;
import com.gcloud.header.network.msg.api.CreateVSwitchMsg;
import com.gcloud.header.network.msg.api.CreateVpcMsg;
import com.gcloud.header.network.msg.api.DeleteVpcMsg;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.Map.Entry;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class NeutronSubnetProvider implements ISubnetProvider {

    @Autowired
    private NeutronProviderProxy proxy;

    @Autowired
    private SubnetDao subnetDao;
    
    @Autowired
    private NetworkDao networkDao;
    
    @Autowired
    private RouterDao routerDao;
    
    @Autowired
    private ISubnetService subnetService;
    
    @Autowired
    private INetworkService networkService;
    
    @Autowired
    private IRouterService routerService;
    
    @Autowired
    private IVpcService vpcService;
    
    @Autowired 
    private IPortService portService;

    @Override
    public ResourceType resourceType() {
        return ResourceType.SUBNET;
    }

    @Override
    public ProviderType providerType() {
        return ProviderType.NEUTRON;
    }

    @Override
    public void createSubnet(Network network, String subnetId, CreateVSwitchParams params, CurrentUser currentUser) {
        SimpleFlowChain<org.openstack4j.model.network.Subnet, String> chain = new SimpleFlowChain<>("createSubnet");
        chain.then(new Flow<org.openstack4j.model.network.Subnet>("createSubnet") {
            @Override
            public void run(SimpleFlowChain chain, org.openstack4j.model.network.Subnet data) {
                org.openstack4j.model.network.Subnet net = proxy.createSubnet(network.getProviderRefId(), params.getvSwitchName(), params.getCidrBlock());
                chain.data(net);
                chain.next();
            }

            @Override
            public void rollback(SimpleFlowChain chain, org.openstack4j.model.network.Subnet data) {
                proxy.deleteSubnet(data.getId());
            }

        }).then(new NoRollbackFlow<org.openstack4j.model.network.Subnet>("save to db") {
            @Override
            public void run(SimpleFlowChain chain, org.openstack4j.model.network.Subnet data) {
                Subnet subnet = new Subnet();
                subnet.setId(subnetId);
                subnet.setCidr(params.getCidrBlock());
                subnet.setName(params.getvSwitchName());
                subnet.setUserId(currentUser.getId());
                subnet.setCreateTime(new Date());
                subnet.setNetworkId(network.getId());
                subnet.setZoneId(params.getRegionId());
                subnet.setProvider(providerType().getValue());
                subnet.setProviderRefId(data.getId());
                subnet.setTenantId(currentUser.getDefaultTenant());
                subnet.setRouterId(params.getVpcId());
                subnetDao.save(subnet);
                chain.next();
            }
        }).done(new FlowDoneHandler<org.openstack4j.model.network.Subnet>() {
            @Override
            public void handle(org.openstack4j.model.network.Subnet data) {
                chain.setResult(data.getId());
            }
        }).start();

        if (StringUtils.isNotBlank(chain.getErrorCode())) {
            throw new GCloudException(chain.getErrorCode());
        }

        /*org.openstack4j.model.network.Subnet net = proxy.createSubnet(params.getVpcId(), params.getvSwitchName(), params.getCidrBlock());
        
        Subnet subnet = new Subnet();
        subnet.setId(net.getId());
        subnet.setCidr(params.getCidrBlock());
        subnet.setName(params.getvSwitchName());
        subnet.setUserId(params.getCurUserId());
        subnet.setCreateTime(new Date());
        subnet.setNetworkId(params.getVpcId());
        subnet.setZoneId(params.getRegionId());
        subnetDao.save(subnet);*/

    }

    @Override
    public void deleteSubnet(String subnetRefId) {   	
    	proxy.deleteSubnet(subnetRefId);   	
    }

    @Override
    public void modifyAttribute(String subnetRefId, String subnetName) {
        proxy.modifySubnetAttribute(subnetRefId, subnetName);
    }

    @Override
    public List<Subnet> list(Map<String, String> filter) {
        List<org.openstack4j.model.network.Subnet> subnets = proxy.listSubnet(filter);
        List<Subnet> retList = new ArrayList<>();
        for (org.openstack4j.model.network.Subnet s : subnets) {
            Subnet sn = new Subnet();
            sn.setCidr(s.getCidr());
            sn.setId(s.getId());
            sn.setName(s.getName());
            sn.setNetworkId(s.getNetworkId());
//            sn.setRouterId();
            sn.setUpdatedAt(s.getUpdatedAt());
            // TODO: other items.

            retList.add(sn);
        }

        return retList;
    }

	@Override
	public String createVSwitch(CreateVSwitchMsg params, CurrentUser currentUser) {
		// TODO Auto-generated method stub
		Network network = networkDao.getById(params.getNetworkId());
    	
    	Router vpc = routerDao.getById(params.getVpcId());
    	
    	String subnetId = UUID.randomUUID().toString();
    	
    	VSwitchProxyData vswitch = new VSwitchProxyData();
    	
        SimpleFlowChain<VSwitchProxyData, String> chain = new SimpleFlowChain<>("createVSwitch");
        chain.then(new NoRollbackFlow<VSwitchProxyData>("createVSwitch") {
            @Override
            public void run(SimpleFlowChain chain, VSwitchProxyData data) {
            	
            	CreateVSwitchParams switchParams = BeanUtil.copyProperties(params, CreateVSwitchParams.class);
            	if (network != null) {
            		createSubnet(network, subnetId, switchParams, currentUser);
        		} else if (vpc != null) {
        			// Create a network
        			CreateVpcMsg vpcParams = BeanUtil.copyProperties(params, CreateVpcMsg.class);
        			vpcParams.setVpcName(String.format("vpcId:%s-network", vpc.getId()));
        			String networkId = networkService.createNetwork(vpcParams);
        			vswitch.setNetworkId(networkId);
        		}
            	  chain.data(vswitch);
            	  chain.next();
            }

        }).then(new Flow<VSwitchProxyData>("createVSwitch") {
            @Override
            public void run(SimpleFlowChain chain, VSwitchProxyData data) {
            	
            	CreateVSwitchParams switchParams = BeanUtil.copyProperties(params, CreateVSwitchParams.class);
            	if (network != null) {
            		
            	}else if (vpc != null) {
    				// Create a subnet  		
            		Network newNetwork = networkDao.getById(data.getNetworkId());
    				createSubnet(newNetwork, subnetId, switchParams, currentUser);
    				vswitch.setSubnetId(subnetId);
        		}
            	  chain.data(vswitch);
            	  chain.next();
            }

            @Override
            public void rollback(SimpleFlowChain chain, VSwitchProxyData data) {
            	String networkId = data.getNetworkId();
            	DeleteVpcMsg msg = new DeleteVpcMsg();
            	msg.setVpcId(networkId);
            	networkService.removeNetwork(msg);
            }
        }).then(new Flow<VSwitchProxyData>("createVSwitch") {
                @Override
                public void run(SimpleFlowChain chain, VSwitchProxyData data) {
                	
                	CreateVSwitchParams switchParams = BeanUtil.copyProperties(params, CreateVSwitchParams.class);
                	if (network != null) {
                		
                	}else if (vpc != null) {
        				// Connect the subnet to the router, make the router connect to
        				// the internet.
//        				Router router = routerDao.getById(data.getNetworkId());
//                		proxy.attachSubnetRouter(vpc.getId(), subnetId);
        				routerService.attachVSwitchVRouter(vpc.getId(), subnetId);
        				
            		}
                	  chain.data(vswitch);
                	  chain.next();
                }

                @Override
                public void rollback(SimpleFlowChain chain, VSwitchProxyData data) {
                	String networkId = data.getNetworkId();
                	String subnetId = data.getSubnetId();
                	DeleteVpcMsg msg = new DeleteVpcMsg();
                	msg.setVpcId(networkId);
//            		deleteSubnet(subnetId);
            		subnetService.deleteSubnet(subnetId);
            		networkService.removeNetwork(msg);
                }
        }).done(new FlowDoneHandler<VSwitchProxyData>() {
            @Override
            public void handle(VSwitchProxyData data) {
                chain.setResult(data.getSubnetId());
            }
        }).start();

        if (StringUtils.isNotBlank(chain.getErrorCode())) {
            throw new GCloudException(chain.getErrorCode());
        }
       
        return subnetId;
	}

	@Override
	public void deleteVSwitch(String subnetRefId) {
		// TODO Auto-generated method stub
		//获取出子网
		Subnet subnet = subnetDao.findUniqueByProperty("provider_ref_id", subnetRefId);
    	org.openstack4j.model.network.Subnet proxySubnet = proxy.getSubnet(subnetRefId);
		//删除网络
		//查找出所有的端口出来，看是否还有端口没释放	
		Map<String, String> needDeleteportIds = new HashMap<>();
		String routerId = null;
		Map<String, String> filter = new HashMap<String, String>();
		filter.put("network_id", proxySubnet.getNetworkId());
		List<org.openstack4j.model.network.Port> ports = proxy.listPort(filter);		
		for(org.openstack4j.model.network.Port port:ports) {
			if("neutron:LOADBALANCERV2".equals(port.getDeviceOwner())) {
				log.debug("删除子网失败存在负载均衡端口没释放"+port.getDeviceId());
				throw new GCloudException("0030403::删除子网失败存在负载均衡端口没释放");
			}else if(port.getDeviceOwner() != null && port.getDeviceOwner().startsWith("compute:")) {
				log.debug("删除子网失败存在实例端口没释放"+port.getDeviceId());
				throw new GCloudException("0030404::删除子网失败存在实例端口没释放");
			}else {
				//寻找路由ID
				if(port.getDeviceOwner() != null && port.getDeviceOwner().startsWith("network:router")) {
					routerId = port.getDeviceId();
					needDeleteportIds.put(port.getId(), routerId);
				}else {
					needDeleteportIds.put(port.getId(), null);
				}
			}
		}
		for(Entry<String, String> kv: needDeleteportIds.entrySet()) {
			if(kv.getValue() != null) {
				Router router = routerDao.findUniqueByProperty("provider_ref_id", kv.getValue());
				routerService.detachVSwitchVRouter(router.getId(), subnet.getId(), kv.getKey());
			}
//			proxy.deletePort(kv.getKey());
		}
		String proxyNetworkId = proxy.getSubnet(subnetRefId).getNetworkId();
		subnetService.deleteSubnet(subnet.getId());
		
		//删掉网络		
		Network network = networkDao.findUniqueByProperty("provider_ref_id", proxyNetworkId);
		if (network.getType() == NetworkType.INTERNAL.getValue()){
			DeleteVpcMsg vpcMsg = new DeleteVpcMsg();
			vpcMsg.setVpcId(network.getId());
			networkService.removeNetwork(vpcMsg);	
		}
		
	}
}
